﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using PropertyManagement.Core.Model;

namespace PropertyManagement.Test.Gen {
    [TestFixture]
    public class GenericDataAccess {
        [Test]
        public void GenDataAccessBase( ) {
            string nsTempalte = @"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Data;
using System.Data.SqlClient;
using PropertyManagement.Core.Model;

namespace PropertyManagement.Core.DataAccess {";

            string classTempate = @"
        public static partial class {0}DataAccess {{
             
            public static {0} GetById(int id){{
                var sql = @""select * from dbo.{0} where {0}ID = @id "";
                var paramList = new[] {{
                    new SqlParameter(""id"", id)
                }};
                using (var iReader = SqlServerHelper.ExecuteReader(SqlServerHelper.ConnectionString, CommandType.Text, sql,
                                                                paramList)) {{
                    if (iReader.Read( )) {{
                            return Creation{0}ByIReader(iReader);
                    }};
                    return null;
                }}
            }}

            public static void Add({0} entity){{
                  var sql = @""insert into dbo.{0} values ({1}) ;   select SCOPE_IDENTITY (); "";
                  var paramList = new SqlParameter[] {{
{2}
                        }};
                  using ( var ireader = SqlServerHelper.ExecuteReader(SqlServerHelper.ConnectionString, CommandType.Text, sql,
                                                            paramList)) {{
                        if (ireader.Read()) {{
                            entity.{0}ID = int.Parse(ireader[0].ToString());
                        }}
                  }}
            
            }}   

            public static void Remove({0} entity) {{
                var sql = @""delete from dbo.{0} where {0}ID = @id"";
                var paramList = new[] {{
                    new SqlParameter(""id"", entity.{0}ID)
                }};
                SqlServerHelper.ExecuteNonQuery(SqlServerHelper.ConnectionString, CommandType.Text, sql, paramList);
            }}   

            public static IEnumerable<{0}> LoadAll(){{
                    var sql = "" select * from dbo.{0} "";
                    var list = new List<{0}>( );
                    using (var iReader = SqlServerHelper.ExecuteReader(SqlServerHelper.ConnectionString, CommandType.Text, sql)) {{
                        while (iReader.Read( )) {{
                                list.Add(Creation{0}ByIReader(iReader));
                        }};
                    }}  
                    return list;
            }}   
        
            public static void Update({0} entity){{
                var sql = @"" update dbo.{0} set {3} where {0}ID = @id "";
                var paramList = new SqlParameter[] {{
                            new SqlParameter(""id"",entity.{0}ID),
{2}
                        }};
                SqlServerHelper.ExecuteNonQuery(SqlServerHelper.ConnectionString, CommandType.Text, sql, paramList);
            }}
            
            public static bool Exists({0} entity){{
                return entity.{0}ID != 0 && GetById(entity.{0}ID)!=null;
            }}


            private static {0} Creation{0}ByIReader(IDataReader iReader){{
                if(iReader!=null){{
                      return new {0}{{
{4}
                        }};
                }}
                return null; 
            }}

            {5}
    }}
";
            string nsEndTempate = "}";

            string submethodTemplate = @"
            
            public static {0} GetByPKs({0} entity){{
                var sql =  @""select *  from dbo.{0} where {1} "";
                var paramList = new SqlParameter[] {{
{2}
                        }};
                 using (var iReader = SqlServerHelper.ExecuteReader(SqlServerHelper.ConnectionString, CommandType.Text, sql,
                                                                paramList)) {{
                    if (iReader.Read( )) {{
                            return Creation{0}ByIReader(iReader);
                    }};
                    return null;
                }}
            }}

            public static bool ExistsByPKs({0} entity){{
                var sql =  @""select *  from dbo.{0} where {1} "";
                var paramList = new SqlParameter[] {{
{2}
                        }};
                 using (var iReader = SqlServerHelper.ExecuteReader(SqlServerHelper.ConnectionString, CommandType.Text, sql,
                                                                paramList)) {{
                    return iReader.Read( );
                }}
            }}
";



            var baseObjectType = typeof (AbstractBaseEntity);
            foreach (var aType in baseObjectType.Assembly.GetTypes()) {
                if (aType.IsClass && !aType.IsAbstract && IsInherit(aType, baseObjectType)) {
                    StringBuilder dal = new StringBuilder( );
                    dal.Append(nsTempalte);
                    var className = aType.Name;
                    dal.AppendFormat(classTempate,
                                     className,
                                     GetParamsNames(aType),
                                     GetParams(aType),
                                     GetParamSetting(aType),
                                     GetCreationEntity(aType),
                                     CreateSubMethods(aType,submethodTemplate)
                        );
                    dal.Append(nsEndTempate);

                    SaveFile("dal", className + "DataAccess.cs", dal);
                }
            }
            
            

            
        }

        private string CreateSubMethods(Type aType, string submethodTemplate) {
           var pkPs = aType.GetProperties()
                 .Where(
                     p => p.CanRead && p.CanWrite && p.GetCustomAttributes(typeof (BPrimaryKeyAttribute), false).Any());
            if (pkPs.Any()) {
                return string.Format(submethodTemplate,
                                     aType.Name,
                                     string.Join(" and ", pkPs.Select(p => p.Name + " = @" + p.Name.ToLower())),
                                     string.Join(",\r\n",
                                                 pkPs.Select(
                                                     p =>
                                                     string.Format(
                                                         "\t\t\t\t\t\t\tnew SqlParameter(\"{0}\",entity.{1})",
                                                         p.Name.ToLower(), p.Name)))
                    );
            }
            return null;
        }

        private void SaveFile(string dir,string fileName, StringBuilder dal) {
            var file = dir + Path.DirectorySeparatorChar + fileName;
            if (!Directory.Exists(dir)) {
                Directory.CreateDirectory(dir);
            }
            if (File.Exists(file)) {
                File.Delete(file);
            }
            File.WriteAllText(file, dal.ToString( ));
        }

        private string GetCreationEntity(Type aType) {
            return string.Join(",\r\n", aType.GetProperties( ).Where(p => p.CanRead && p.CanWrite).OrderBy(p => p.Name)
                                         .Select(p => {
                                             var pType = p.PropertyType;
                                             return
                                                 string.Format(
                                                     "\t\t\t\t\t\t\t{0} = iReader[\"{0}\"] is DBNull ? default({1}) : ({1})iReader[\"{0}\"] ",
                                                     p.Name,
                                                     pType.Name
                                                     );
                                         }));
        }

        private string GetParamSetting(Type aType) {
            var className = aType.Name;
            return string.Join(",", aType.GetProperties( ).Where(p => p.Name != className + "ID" && p.CanRead && p.CanWrite).OrderBy(p => p.Name)
                                         .Select(p => string.Format("{1}=@{0}", p.Name.ToLower( ), p.Name)));
        }

        private string GetParams(Type aType) {
            var className = aType.Name;
            return string.Join(",\r\n", aType.GetProperties( ).Where(p => p.Name != className + "ID" && p.CanRead && p.CanWrite).OrderBy(p => p.Name)
                                         .Select(p => string.Format("\t\t\t\t\t\t\tnew SqlParameter(\"{0}\",entity.{1})", p.Name.ToLower( ), p.Name)));
        }

        private string GetParamsNames(Type aType) {
            var className = aType.Name;
            return string.Join(",", aType.GetProperties().Where(p => p.Name!=className+"ID" && p.CanRead && p.CanWrite)
                                         .Select(p => "@" + p.Name.ToLower()));
        }

        private bool IsInherit(Type aType, Type appointedType) {
            Type baseType = aType.BaseType;
            while (baseType != null && baseType != typeof(Object)) {
                if (baseType == appointedType) return true;
                baseType = baseType.BaseType;
            }
            return false;
        }
    }
}
